#include "peer_connection_channel.h"
#include "api/create_peerconnection_factory.h"
#include "modules/audio_device/include/audio_device_data_observer.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "rtc_base/thread.h"
#include "rtc_client/peer_connection_dependency_factory.h"
#include "rtc_client/rtc_log.h"
#include "signaling_client.h"
#include "sdk/ohos/create_ohos_peerconnection_factory.h"

namespace ohosrtc {

std::shared_ptr<PeerConnectionChannel>
PeerConnectionChannel::Create(PeerConnectionChannelConfiguration configuration,
                              std::shared_ptr<PeerConnectionDependencyFactory> pcd_factory,
                              std::shared_ptr<SignalingClient> signaling_client) {
  auto pcc = std::make_shared<PeerConnectionChannel>(configuration, pcd_factory, signaling_client);
  pcc->weak_this_ = pcc;
  return pcc;
}

PeerConnectionChannel::PeerConnectionChannel(PeerConnectionChannelConfiguration configuration,
                                             std::shared_ptr<PeerConnectionDependencyFactory> pcd_factory,
                                             std::shared_ptr<SignalingClient> signaling_client)
    : configuration_(std::move(configuration)) {
  RTC_LOG << "PeerConnectionChannel::Ctor";
  pcd_factory_ = pcd_factory;
  signaling_client_ = signaling_client;
  InitPeerConnection();
}

PeerConnectionChannel::~PeerConnectionChannel() {
  RTC_LOG << "PeerConnectionChannel::Dtor";
}

void PeerConnectionChannel::InitPeerConnection() {
  if (!pcd_factory_) {
    return;
  }
  RTC_LOG << "init";
  ohos_peer_connection_ = pcd_factory_->CreateOHOSPeerConnection(configuration_, this);
}

void PeerConnectionChannel::CreateP2pOffer(OHOSLocalStreamPtr ohos_local_stream) {
  RTC_LOG << "create p2p offer";
  ohos_local_stream_ = ohos_local_stream;
  // create offer
  CreateOffer();
}

void PeerConnectionChannel::CreateP2pAnswer(OHOSLocalStreamPtr ohos_local_stream) {
  RTC_LOG << "create p2p answer";
  ohos_local_stream_ = ohos_local_stream;
  // create answer
  CreateAnswer();
}

void PeerConnectionChannel::setLocalStream(OHOSLocalStreamPtr stream){
  ohos_local_stream_ = stream;
}

void PeerConnectionChannel::RemoveStream(OHOSMediaStreamPtr media_stream){
  RTC_LOG << "remove stream start";
  if (!ohos_peer_connection_) {
    RTC_LOG << "remove stream error no pc";
    return;
  }
  std::string sid = media_stream->stream_id();
  auto senders = stream_sends_[sid];
  for(const auto& sender: senders){
     ohos_peer_connection_->RemoveTrack(sender);
  }
  stream_sends_.erase(sid);
}

void PeerConnectionChannel::AddStream(OHOSMediaStreamPtr media_stream) {
  RTC_LOG << "add stream start" << media_stream->stream_id();
  
  if (!ohos_peer_connection_) {
    RTC_LOG << "add stream error no pc";
    return;
  }
    
  auto audio_tracks = media_stream->GetAudioTracks();
  std::vector<std::string> stream_ids;
  std::string sid = media_stream->stream_id();
  std::vector<rtc::scoped_refptr<ohoswebrtc::OHOSRtpSenderInterface>> senders;
  stream_ids.push_back(sid);
  for (auto audio_track : audio_tracks) {
    RTC_LOG << "add audio track";
    auto sender = ohos_peer_connection_->AddTrack(audio_track, stream_ids);
    senders.emplace_back(sender);
  }

  auto video_tracks = media_stream->GetVideoTracks();
  for (auto video_track : video_tracks) {
    RTC_LOG << "add video track";
    auto sender = ohos_peer_connection_->AddTrack(video_track, stream_ids);
    senders.emplace_back(sender);
  }
  stream_sends_[sid] = senders;  
}

void PeerConnectionChannel::CreateOffer() {
  RTC_LOG << "create offer start";
  if (!ohos_peer_connection_) {
    RTC_LOG << "create offer error no pc";
    return;
  }
  auto media_constraints = webrtc::CreateOHOSMediaConstraints({{"OfferToReceiveAudio", "true"}, {"OfferToReceiveVideo","true"}},{});
  auto observer = MakeCreateSdpObserver(weak_this_);
  ohos_peer_connection_->CreateOffer(observer, media_constraints);
}

void PeerConnectionChannel::CreateAnswer() {
  RTC_LOG << "create answer start";
  if (!ohos_peer_connection_) {
    RTC_LOG << "create offer error no pc";
    return;
  }
  auto media_constraints = webrtc::CreateOHOSMediaConstraints({{"OfferToReceiveAudio", "true"}, {"OfferToReceiveVideo","true"}},{});
  auto observer = MakeCreateSdpObserver(weak_this_);
  ohos_peer_connection_->CreateAnswer(observer, media_constraints);
}

void PeerConnectionChannel::SetLocalDescription(SDP *desc) {
  if (!ohos_peer_connection_) {
    RTC_LOG << "pc not exist";
    return;
  }
  
  std::string sdp;
  desc->ToString(&sdp);
  auto observer = MakeSetLocalDescriptionObserver(weak_this_);
  ohos_peer_connection_->SetLocalDescription(sdp, desc->GetType(),observer);
  
  if (!signaling_client_) {
    RTC_LOG << "signaling client not exist";
    return;
  }
  if (desc->GetType() == webrtc::SdpType::kOffer) {
    signaling_client_->SendOfferSdp(*desc);
  } else if (desc->GetType() == webrtc::SdpType::kAnswer) {
    signaling_client_->SendAnswerSdp(*desc);
  }
}

void PeerConnectionChannel::SetRemoteDescription(SDP* desc) {
  if (!ohos_peer_connection_) {
    RTC_LOG << "pc not exist";
    return;
  }
  
  RTC_LOG << "sdp type " << (std::to_string(desc->GetType() == webrtc::SdpType::kOffer));
  std::string sdp;
  desc->ToString(&sdp);
  auto observer = MakeSetRemoteDescriptionObserver(weak_this_);
  ohos_peer_connection_->SetRemoteDescription(sdp, desc->GetType(), observer);
}

void PeerConnectionChannel::SetRemoteIceCandidate(IceCandidate* ice_candidate) {
  RTC_LOG << "set ice candidate";
  if (!ohos_peer_connection_) {
    RTC_LOG << "pc not exist";
    return;
  }
  
  ohos_peer_connection_->AddIceCandidate(ice_candidate);
}

void PeerConnectionChannel::Close() {
  if (!ohos_peer_connection_) {
    RTC_LOG << "pc not exist";
    return;
  }
  RTC_LOG << "PeerConnectionChannel::Close()";
  ohos_peer_connection_->Close();
}

OHOSPeerConnectionPtr PeerConnectionChannel::GetPeerConnection() {
  return ohos_peer_connection_;
}

void PeerConnectionChannel::SetStreamEventHandler(OnAddStreamEvent on_add_stream, OnRemoveStreamEvent on_remove_stream) {
	RTC_LOG << "set stream event handler";
	on_add_stream_event_ = on_add_stream;
	on_remove_stream_event_ = on_remove_stream;
}

void PeerConnectionChannel::OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) {
  RTC_LOG << "new state=" << std::to_string(new_state);
}

void PeerConnectionChannel::OnAddStream(rtc::scoped_refptr<ohoswebrtc::OHOSMediaStreamInterface> stream) {
  RTC_LOG << "OnAddStream stream_id=" << stream->stream_id() << " audio_tracks=" << stream->GetAudioTracks().size() << " video_tracks=" << stream->GetVideoTracks().size();
  RTC_LOG << "on stream add, stream_id=" << stream->stream_id();
	if (on_add_stream_event_) {
		on_add_stream_event_(std::move(stream));
	}
}

void PeerConnectionChannel::OnRemoveStream(rtc::scoped_refptr<ohoswebrtc::OHOSMediaStreamInterface> stream) {
  RTC_LOG << "on stream remove, stream_id=" << stream->stream_id();
	if (on_remove_stream_event_) {
		on_remove_stream_event_(std::move(stream));
	}
}

void PeerConnectionChannel::OnDataChannel(rtc::scoped_refptr<ohoswebrtc::OHOSDataChannelInterface> data_channel) {
  RTC_LOG << "data_channel=" << data_channel->id();
}

void PeerConnectionChannel::OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) {
  RTC_LOG << "ice_connection_state=" << std::to_string(new_state);
}

void PeerConnectionChannel::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state){
  RTC_LOG << "ice_gather_state=" << std::to_string(new_state);
}

void PeerConnectionChannel::OnIceCandidate(const webrtc::IceCandidateInterface *candidate) {
  RTC_LOG << "ice_candidate=" << candidate->sdp_mid();
  signaling_client_->SendLocalIceCandidate(*candidate);
}

void PeerConnectionChannel::OnAddTrack(rtc::scoped_refptr<ohoswebrtc::OHOSRtpReceiverInterface> receiver,
                                       const std::vector<rtc::scoped_refptr<ohoswebrtc::OHOSMediaStreamInterface>> &streams) {
  RTC_LOG << "PeerConnectionChannel::OnAddTrack";
}

FunctionalCreateSdpObserver *
PeerConnectionChannel::MakeCreateSdpObserver(const std::weak_ptr<PeerConnectionChannel> &weak_this) {
  auto *observer = new rtc::scoped_refptr<FunctionalCreateSdpObserver>();
  auto on_success = [weak_this, observer](SDP *desc) {
    auto shared_this = weak_this.lock();
    if (shared_this) {
      shared_this->OnCreateSdpSuccess(desc);
    }
  };
  auto on_failure = [weak_this, observer](const std::string &error) {
   auto shared_this = weak_this.lock();
    if (shared_this) {
      shared_this->OnCreateSdpFailure(error);
    }
  };
  *observer = FunctionalCreateSdpObserver::Create(std::move(on_success),
                                                                 std::move(on_failure));
  return observer->get();
}

rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface> 
PeerConnectionChannel::MakeSetLocalDescriptionObserver(const std::weak_ptr<PeerConnectionChannel> &weak_this) {
  auto on_completion = [weak_this](const webrtc::RTCError error) {
   auto shared_this = weak_this.lock();
    if (shared_this) {
      shared_this->OnSetLocalSdpCompletion(error);
    }
  };
  auto observer = FunctionalSetLocalDescriptionObserver::Create(std::move(on_completion));
  return observer;
}

rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface> 
PeerConnectionChannel::MakeSetRemoteDescriptionObserver(const std::weak_ptr<PeerConnectionChannel> &weak_this) {
  auto on_completion = [weak_this](const webrtc::RTCError error) {
   auto shared_this = weak_this.lock();
    if (shared_this) {
      shared_this->OnSetRemoteSdpCompletion(error);
    }
  };
  auto observer = FunctionalSetRemoteDescriptionObserver::Create(std::move(on_completion));
  return observer;
}

void PeerConnectionChannel::OnCreateSdpSuccess(SDP *desc) {
  std::string out;
  desc->ToString(&out);
//   RTC_LOG << " SDP address:"<<(uint64_t)(desc) << "out size: "<<out.size();
  RTC_LOG << "create local sdp success \n" ;//<< out;
  SetLocalDescription(desc);
}

void PeerConnectionChannel::OnCreateSdpFailure(const std::string error) {
  RTC_LOG << "create local sdp failure " << error;
}

void PeerConnectionChannel::OnSetLocalSdpCompletion(const webrtc::RTCError error) { RTC_LOG << "set local sdp success"; }

void PeerConnectionChannel::OnSetRemoteSdpCompletion(const webrtc::RTCError error) { 
  RTC_LOG << "set remote desc not error" << error.message();
  if (error.ok()) {
    RTC_LOG << "set remote sdp success";
    if (!signaling_client_) {
      RTC_LOG << "no signaling client";
      return;
    }
    auto access_info = this->signaling_client_->GetRoomAccessInfo();
    CreateP2pAnswer(ohos_local_stream_);
  }
}

} // namespace ohosrtc